﻿//Copyright © Steffen Zeidler 2010
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using System.Reactive;
using System.Reactive.Linq;

namespace MarbleDiagramGenerator
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            point1 = FindResource("Point1") as DataTemplate;
            point2 = FindResource("Point2") as DataTemplate;
            label1 = FindResource("Label1") as DataTemplate;
            Application.Current.DispatcherUnhandledException += DispatcherUnhandledExceptionEventHandler;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Expression1.ItemsSource = File.ReadAllLines("Samples.txt");
            Expression1.Focus();
        }

        class Point1// : TraceRecord
        {
            public object Symbol { get; internal set; }
            public object Text { get; internal set; }
            public object Label { get; internal set; }
            public double X { get; internal set; }
            public double Y { get; internal set; }
            public object Brush { get; internal set; }
        }

        private void OnTrace(ObservableTrace.TraceNotificationEx r)
        {
            if (r.x * 10 >= Diagram1.Width || r.y * 10 >= Diagram1.Height)
            {
                Reset();
                return;
            }
            //if (r.x2 > 0)
            //{
            //    double y = Diagram1.Height - r.y * 10;
            //    Diagram1.Children.Add(new Line() { X1 = r.x * 10, X2 = r.x2 * 10, Y1 = y, Y2 = y, Stroke = Brushes.Blue });
            //    return;
            //}
            DataTemplate dt = point1;
            Point1 p1 = new Point1() { Brush = Brushes.Black, X = r.x * 10, Y = r.y * 10, Text = r.method };
            switch (r.method)
            {
                case "OnNext":
                    dt = point2;
                    p1.Text = r.p[0];
                    if (values.ContainsKey(p1.Text))
                    {
                        p1.Brush = values[p1.Text];
                    }
                    else
                    {
                        lastBrush = (lastBrush + 1) % brushes.Length;
                        p1.Brush = values[p1.Text] = brushes[lastBrush];
                    }
                    if (r.idx == 0)
                    {
                        p1.Label = p1.Text;
                    }
                    break;
                case "OnError":
                    p1.Text = r.p[0];
                    p1.Symbol = "X";
                    break;
                case "OnCompleted":
                    p1.Symbol = "|";
                    break;
                case "Subscribe":
                    {
                        p1.Symbol = "(";
                        var l1 = new Point1() { Y = p1.Y, Label = r.combinator, Text = r.expression, Brush = Brushes.Black };
                        Diagram1.Children.Add(new ContentControl() { Content = l1, ContentTemplate = label1, IsTabStop = false });
                    }
                    break;
                case "Dispose":
                    p1.Symbol = ")";
                    break;
            }
            Diagram1.Children.Add(new ContentControl() { Content = p1, ContentTemplate = dt, IsTabStop = false });
        }

        private void Reset()
        {
            if (disposableTrace != null)
            {
                disposableTrace.Dispose();
            }
            values.Clear();
            lastBrush = 0;
        }

        private void buttonStart_Click(object sender, RoutedEventArgs e)
        {
            Reset();
            if (ObservableTrace.IsDeadlock())
            {
                Errors1.Text = "Deadlock!";
                Errors1.Visibility = Visibility.Visible;
                return;
            }
            Diagram1.Children.Clear();
            CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
            CompilerParameters compilerParameter = new CompilerParameters(new[]
                { 
                    "System.dll", 
                    "System.Core.dll", 
                    typeof(Observable).Assembly.Location, //System.Reactive
                    //typeof(EnumerableEx).Assembly.Location, //System.Interactive
                }) { GenerateInMemory = true };
            string source = string.Concat(
                "using System;",
                "using System.Linq;",
                "using System.Linq.Expressions;", 
                "using System.Reactive.Concurrency;",
                "using System.Reactive.Linq;",
                "class Expr {",
                //"static TimeSpan s = TimeSpan.FromSeconds(1);",
                "static void N(){}",
                "public static Expression<Func<IDisposable>> expr1 = () => (",
                Expression1.Text,
                ").Subscribe(x => N(), e => N(), N);}");
            CompilerResults compilerRes = provider.CompileAssemblyFromSource(compilerParameter, source);
            if (compilerRes.NativeCompilerReturnValue != 0)
            {
                Grid1.Visibility = Visibility.Hidden;
                Errors1.Visibility = Visibility.Visible;
                StringBuilder text = new StringBuilder();
                foreach (CompilerError error in compilerRes.Errors)
                {
                    text.Append(error.ErrorText);
                    text.Append("\n");
                }
                Errors1.Text = text.ToString();
                return;
            }
            else
            {
                Grid1.Visibility = Visibility.Visible;
                Errors1.Visibility = Visibility.Collapsed;
                Errors1.Text = "";
            }
            object expr1 = compilerRes.CompiledAssembly.GetType("Expr").GetField("expr1").GetValue(null);
            var trace = ObservableTrace.Trace(expr1 as System.Linq.Expressions.Expression<Func<IDisposable>>);
            disposableTrace = trace.ObserveOnDispatcher().Subscribe(OnTrace);
        }

        void DispatcherUnhandledExceptionEventHandler(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            Errors1.Text = e.Exception.Message;
            Grid1.Visibility = Visibility.Hidden;
            Errors1.Visibility = Visibility.Visible;
            e.Handled = true;
        }

        readonly DataTemplate point1;
        readonly DataTemplate point2;
        readonly DataTemplate label1;
        readonly SolidColorBrush[] brushes = {
             Brushes.Black, Brushes.Blue, Brushes.Brown, Brushes.DarkBlue, Brushes.Green, Brushes.Red, Brushes.DarkMagenta, Brushes.Orange,
             Brushes.BlueViolet, Brushes.Chartreuse, Brushes.Coral, Brushes.DarkGreen, Brushes.DarkRed, Brushes.Fuchsia, Brushes.Goldenrod, Brushes.GreenYellow,
             Brushes.HotPink, Brushes.IndianRed, Brushes.Indigo, Brushes.LimeGreen, Brushes.Magenta, Brushes.Maroon, Brushes.Olive, Brushes.OrangeRed,};
        private Dictionary<object, Brush> values = new Dictionary<object, Brush>();
        private int lastBrush;
        private IDisposable disposableTrace;
    }
}
